package study.springcloud.gateway.filter.global;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import study.springcloud.gateway.support.utils.RouteUtils;
import study.springcloud.gateway.support.utils.WebUtils;

/**
 * （1）请求体的流只能被读取一次
 * （2）使用 ServerHttpRequestDecorator 来替换原来的请求，保证请求体的流能够被后续的过滤器和路由处理器正常读取
 * （3）这个方法可能会对性能产生影响，因为它会读取整个请求体到内存中。
 * 如果你的应用需要处理大量的大文件上传，这可能会导致内存溢出。
 * 因此，只有当你确实需要读取请求体的内容时，才应该使用这种方法。
 * <p>
 * https://blog.csdn.net/lifetime_gear/article/details/136379521
 */
@Slf4j
@Component
public class ReadBodyFilter implements GlobalFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //
        ServerHttpRequest request = WebUtils.getServerHttpRequest(exchange);
        ServerHttpResponse response = WebUtils.getServerHttpResponse(exchange);
        log.info("请求 [{}] 转发给 [{}, {}]",
                RouteUtils.getOriginalRequestPath(exchange),
                RouteUtils.getGatewayRequestHost(exchange),
                RouteUtils.getGatewayRequestPath(exchange));
        //
        Flux<DataBuffer> body = request.getBody();
        //
        return DataBufferUtils.join(body)
                .flatMap(dataBuffer -> {
                    // 读取请求体
                    byte[] bytes = new byte[dataBuffer.readableByteCount()];
                    dataBuffer.read(bytes);

                    // 打印
                    String bodyStr = StrUtil.str(bytes, "UTF-8");
                    log.info(">>>>>> Request body: {}", StrUtil.removeAny(bodyStr, "\n", "\t"));

                    // 装饰 ServerHttpRequest 对象
                    Flux<DataBuffer> cachedFlux = Flux.just(response.bufferFactory().wrap(bytes));
                    ServerHttpRequest decorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
                        @Override
                        public Flux<DataBuffer> getBody() {
                            return cachedFlux;
                        }
                    };

                    // 生成 ServerWebExchange
                    ServerWebExchange newExchange = exchange.mutate()
                            .request(decorator)
                            .build();
                    //
                    return chain.filter(newExchange);
                });
    }
}
